## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.1 ✔ tibble 3.2.1
## ✔ lubridate 1.9.4 ✔ tidyr 1.3.1
## ✔ purrr 1.0.4
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
## Loading required package: carData
##
## Attaching package: 'car'
##
## The following object is masked from 'package:dplyr':
##
## recode
##
## The following object is masked from 'package:purrr':
##
## some
##
## Attaching package: 'scales'
##
## The following object is masked from 'package:purrr':
##
## discard
##
## The following object is masked from 'package:readr':
##
## col_factor
## Rows: 49244 Columns: 35
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (9): TaskID, ActionID, distance, direction, complexity, zoomDirection,...
## dbl (22): UserID, main_translation_x, main_translation_y, main_translation_...
## lgl (3): rotateGlobeWhileDragging, oneHandedRotationGesture, moveGlobeWhil...
## dttm (1): Date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): Timestamp, Academic_level, Gender, Age_group, Exp_ARVR, Globe_usage...
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Positioning_preference, Positioning_feedback
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Rotation_preference, Rotation_feedback
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Scale_preference, Scale_feedback
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 12 Columns: 6
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (5): Timestamp, Combined_positioning_preference, Combined_rotation_prefe...
## dbl (1): UserID
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## UserID TaskID ActionID
## Min. : 1.000 Length:49244 Length:49244
## 1st Qu.: 4.000 Class :character Class :character
## Median : 7.000 Mode :character Mode :character
## Mean : 6.741
## 3rd Qu.:10.000
## Max. :12.000
## rotateGlobeWhileDragging oneHandedRotationGesture moveGlobeWhileScaling
## Mode :logical Mode :logical Mode :logical
## FALSE:36803 FALSE:11933 FALSE:46552
## TRUE :12441 TRUE :37311 TRUE :2692
##
##
##
## distance direction complexity zoomDirection
## Length:49244 Length:49244 Length:49244 Length:49244
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
## Date Type ActionStatus
## Min. :2025-04-23 05:27:13.00 Length:49244 Length:49244
## 1st Qu.:2025-04-25 01:36:58.00 Class :character Class :character
## Median :2025-04-26 00:45:01.00 Mode :character Mode :character
## Mean :2025-04-27 21:46:53.98
## 3rd Qu.:2025-05-01 07:26:51.00
## Max. :2025-05-05 23:37:33.00
## main_translation_x main_translation_y main_translation_z main_rotation_x
## Min. :-7.099065 Min. :-0.3298 Min. :-3.487 Min. :-0.97540
## 1st Qu.:-0.400000 1st Qu.: 0.9000 1st Qu.:-1.921 1st Qu.:-0.03161
## Median :-0.004060 Median : 0.9000 Median :-1.500 Median : 0.00000
## Mean :-0.005048 Mean : 1.2326 Mean :-1.683 Mean :-0.03896
## 3rd Qu.: 0.400000 3rd Qu.: 1.5539 3rd Qu.:-1.500 3rd Qu.: 0.00000
## Max. : 3.256168 Max. : 3.8304 Max. : 5.006 Max. : 0.97834
## main_rotation_y main_rotation_z main_rotation_w main_scale_x
## Min. :-1.0000 Min. :-0.97710 Min. :-0.9997261 Min. :0.08431
## 1st Qu.:-0.2033 1st Qu.: 0.00000 1st Qu.: 0.0000001 1st Qu.:0.99989
## Median : 0.9601 Median : 0.00000 Median : 0.0626987 Median :1.00000
## Mean : 0.5003 Mean : 0.01287 Mean : 0.2756917 Mean :0.99575
## 3rd Qu.: 1.0000 3rd Qu.: 0.00000 3rd Qu.: 0.6346812 3rd Qu.:1.00002
## Max. : 1.0000 Max. : 0.98922 Max. : 0.9999814 Max. :7.69231
## main_scale_y main_scale_z target_translation_x target_translation_y
## Min. :0.08431 Min. :0.08431 Min. :-3.10000 Min. :0.613
## 1st Qu.:0.99994 1st Qu.:0.99990 1st Qu.:-0.40000 1st Qu.:0.900
## Median :1.00000 Median :1.00000 Median : 0.00000 Median :0.900
## Mean :0.99577 Mean :0.99576 Mean :-0.02449 Mean :1.245
## 3rd Qu.:1.00002 3rd Qu.:1.00002 3rd Qu.: 0.40000 3rd Qu.:1.773
## Max. :7.69231 Max. :7.69231 Max. : 2.33777 Max. :2.547
## target_translation_z target_rotation_x target_rotation_y target_rotation_z
## Min. :-3.3210 Min. :-0.3928 Min. :-0.6935 Min. :-0.21194
## 1st Qu.:-1.9598 1st Qu.:-0.3584 1st Qu.:-0.5655 1st Qu.: 0.00000
## Median :-1.5000 Median : 0.0000 Median : 1.0000 Median : 0.00000
## Mean :-1.6971 Mean :-0.1153 Mean : 0.3768 Mean :-0.01644
## 3rd Qu.:-1.5000 3rd Qu.: 0.0000 3rd Qu.: 1.0000 3rd Qu.: 0.00000
## Max. :-0.8953 Max. : 0.0000 Max. : 1.0000 Max. : 0.13795
## target_rotation_w target_scale_x target_scale_y target_scale_z
## Min. :-0.9761015 Min. :0.1700 Min. :0.1700 Min. :0.1700
## 1st Qu.: 0.0000001 1st Qu.:1.0000 1st Qu.:1.0000 1st Qu.:1.0000
## Median : 0.0000001 Median :1.0000 Median :1.0000 Median :1.0000
## Mean : 0.2914215 Mean :0.9946 Mean :0.9946 Mean :0.9946
## 3rd Qu.: 0.7119398 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:1.0000
## Max. : 0.9807853 Max. :2.0000 Max. :2.0000 Max. :2.0000
## match_accuracy_result status
## Min. : 0.00000 Length:49244
## 1st Qu.: 0.00000 Class :character
## Median : 0.00000 Mode :character
## Mean : 0.03784
## 3rd Qu.: 0.00000
## Max. :22.31002
## UserID Timestamp Academic_level Gender
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## Age_group Exp_ARVR Globe_usage_frequency
## Length:12 Length:12 Length:12
## Class :character Class :character Class :character
## Mode :character Mode :character Mode :character
##
##
##
## Have_used_VisionPro
## Length:12
## Class :character
## Mode :character
##
##
##
## UserID Timestamp Mentally_demanding Physically_demanding
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Mentally_demanding Physically_demanding
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Positioning_preference Positioning_feedback
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Mentally_demanding Physically_demanding
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Mentally_demanding Physically_demanding
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Rotation_preference Rotation_feedback
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Mentally_demanding Physically_demanding
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Mentally_demanding Physically_demanding
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Scale_preference Scale_feedback
## Min. : 1.00 Length:12 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character Class :character
## Median : 6.50 Mode :character Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## UserID Timestamp Combined_positioning_preference
## Min. : 1.00 Length:12 Length:12
## 1st Qu.: 3.75 Class :character Class :character
## Median : 6.50 Mode :character Mode :character
## Mean : 6.50
## 3rd Qu.: 9.25
## Max. :12.00
## Combined_rotation_preference Combined_scale_preference Combined_feedback
## Length:12 Length:12 Length:12
## Class :character Class :character Class :character
## Mode :character Mode :character Mode :character
##
##
##
## [1] 12
# Participants' gender distribution
demographic.gender <- demographic %>%
select(UserID, Gender) %>%
distinct() %>%
group_by(Gender) %>%
summarise(count = n()) %>%
mutate(percentage = round(count / sum(count) * 100, 1), percentage = paste0(percentage, "%"))
demographic.gender## # A tibble: 2 × 3
## Gender count percentage
## <chr> <int> <chr>
## 1 Man 10 83.3%
## 2 Woman 2 16.7%
# Participants' gender distribution chart
ggplot(demographic.gender, aes(x = "", y = count, fill = Gender)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = percentage), position = position_stack(vjust = 0.5), size = 4) +
labs(title = "Distribution of Participants' Gender") +
theme_void()# Participants' academic level distribution
demographic.academic_level <- demographic %>%
select(UserID, Academic_level) %>%
distinct() %>%
group_by(Academic_level) %>%
summarise(count = n()) %>%
mutate(percentage = round(count / sum(count) * 100, 1), graph_label = paste0(percentage, "%")) %>%
rename(`Academic levels` = Academic_level)
demographic.academic_level## # A tibble: 3 × 4
## `Academic levels` count percentage graph_label
## <chr> <int> <dbl> <chr>
## 1 Graduate Student 10 83.3 83.3%
## 2 Postdoctoral Researcher 1 8.3 8.3%
## 3 Undergraduate Student 1 8.3 8.3%
# Participants' academic level distribution chart
ggplot(demographic.academic_level, aes(x = "", y = count, fill = `Academic levels`)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = graph_label), position = position_stack(vjust = 0.5), size = 4) +
labs(title = "Distribution of Participants' Academic Level") +
theme_void() # Participants' previous AR/VR experience distribution
demographic.ARVR_exp <- demographic %>%
select(UserID, Exp_ARVR ) %>%
distinct() %>%
group_by(Exp_ARVR) %>%
summarise(count = n()) %>%
mutate(percentage = round(count / sum(count) * 100, 1),
label = paste0(percentage, "%"),
ShortLabel = fct_recode(Exp_ARVR,
"No experience" = "I have no experience")
) %>%
rename(`Previous AR/VR experience` = ShortLabel)
demographic.ARVR_exp## # A tibble: 3 × 5
## Exp_ARVR count percentage label Previous AR/VR exper…¹
## <chr> <int> <dbl> <chr> <fct>
## 1 Beginner (less than 5 hours exp… 4 33.3 33.3% Beginner (less than 5…
## 2 Familiar (5-20 hours experience) 3 25 25% Familiar (5-20 hours …
## 3 I have no experience 5 41.7 41.7% No experience
## # ℹ abbreviated name: ¹`Previous AR/VR experience`
# Participants' previous AR/VR experience distribution chart
ggplot(demographic.ARVR_exp, aes(x = "", y = count, fill = `Previous AR/VR experience`)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = label), position = position_stack(vjust = 0.5), size = 4) +
labs(title = "Distribution of Participants Previous AR/VR Experience") +
theme_void() # Participants' previous globe experience distribution
demographic.globes_exp <- demographic %>%
select(UserID, Globe_usage_frequency) %>%
distinct() %>%
group_by(Globe_usage_frequency) %>%
summarise(count = n()) %>%
mutate(percentage = round(count / sum(count) * 100, 1),
graph_label = paste0(percentage, "%")) %>%
rename(`Previous globes experience` = Globe_usage_frequency)
demographic.globes_exp## # A tibble: 3 × 4
## `Previous globes experience` count percentage graph_label
## <chr> <int> <dbl> <chr>
## 1 A few times a month 1 8.3 8.3%
## 2 A few times a year 3 25 25%
## 3 Once every few years 8 66.7 66.7%
# Participants' previous globe experience distribution chart
ggplot(demographic.globes_exp, aes(x = "", y = count, fill = `Previous globes experience`)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = graph_label), position = position_stack(vjust = 0.5), size = 4) +
labs(title = "Distribution of Participants Previous AR/VR Experience") +
theme_void() # Participants' previous Apple Vision Pro Experience distribution
demographic.visionpro_exp <- demographic %>%
select(UserID, Have_used_VisionPro) %>%
distinct() %>%
group_by(Have_used_VisionPro) %>%
summarise(count = n()) %>%
mutate(
percentage = round(count / sum(count) * 100, 1),
graph_label = paste0(percentage, "%")
) %>%
rename(`Have used Apple Vision Pro` = Have_used_VisionPro)
demographic.visionpro_exp## # A tibble: 2 × 4
## `Have used Apple Vision Pro` count percentage graph_label
## <chr> <int> <dbl> <chr>
## 1 I have never used the Apple Vision Pro 11 91.7 91.7%
## 2 I have used the Apple Vision Pro once or twice 1 8.3 8.3%
# Participants' previous Apple Vision Pro Experience distribution chart
ggplot(demographic.visionpro_exp, aes(x = "", y = count, fill = `Have used Apple Vision Pro`)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = graph_label), position = position_stack(vjust = 0.5), size = 4) +
labs(title = "Distribution of Participants Previous AR/VR Experience") +
theme_void() data.positioning <- data %>%
mutate(positionCondition = if_else(rotateGlobeWhileDragging, "rotatingGlobe", "nonRotatingGlobe")) %>%
select(-rotateGlobeWhileDragging) %>%
inner_join(demographic, by = "UserID") %>%
inner_join(positioning_NRG, by = "UserID") %>%
rename(
PAAS_NRG = Mentally_demanding,
BORG_NRG = Physically_demanding
) %>%
mutate(
PAAS_NRG = as.numeric(str_extract(PAAS_NRG, "\\d+(\\.\\d+)?")),
BORG_NRG = as.numeric(str_extract(BORG_NRG, "\\d+(\\.\\d+)?"))
) %>%
mutate(
PAAS_NRG = if_else(positionCondition == "nonRotatingGlobe", PAAS_NRG, NA_real_),
BORG_NRG = if_else(positionCondition == "nonRotatingGlobe", BORG_NRG, NA_real_)
) %>%
inner_join(positioning_RG, by = "UserID") %>%
rename(
PAAS_RG = Mentally_demanding,
BORG_RG = Physically_demanding
) %>%
mutate(
PAAS_RG = as.numeric(str_extract(PAAS_RG, "\\d+(\\.\\d+)?")),
BORG_RG = as.numeric(str_extract(BORG_RG, "\\d+(\\.\\d+)?"))
) %>%
mutate(
PAAS_RG = if_else(positionCondition == "rotatingGlobe", PAAS_RG, NA_real_),
BORG_RG = if_else(positionCondition == "rotatingGlobe", BORG_RG, NA_real_)
) %>%
inner_join(positioning_preference, by = "UserID") %>%
rename(
behaviour_preference = Positioning_preference,
behaviour_feedback = Positioning_feedback
) %>%
mutate(
behaviour_preference = case_when(
str_detect(behaviour_preference, "Static orientation") ~ "staticOrientation",
str_detect(behaviour_preference, "Adaptive orientation") ~ "adaptiveOrientation",
str_detect(behaviour_preference, "no preference") ~ "noPreference",
TRUE ~ "unknown"
) ) %>%
filter(Type == "positionTask") %>%
select(UserID, TaskID, ActionID, positionCondition, distance, direction, Date, ActionStatus, main_translation_x,
main_translation_y, main_translation_z, target_translation_x, target_translation_y, target_translation_z,
match_accuracy_result, status, PAAS_NRG, BORG_NRG, PAAS_RG, BORG_RG, behaviour_preference, behaviour_feedback) %>%
mutate(distance = as.factor(distance),
direction = as.factor(direction),
positionCondition = as.factor(positionCondition),
status = as.factor(status),
behaviour_preference = as.factor(behaviour_preference))data.positioning.matched <- data.positioning %>%
filter(status == "Matched")
shapiro.test(data.positioning.matched$match_accuracy_result)##
## Shapiro-Wilk normality test
##
## data: data.positioning.matched$match_accuracy_result
## W = 0.97029, p-value = 2.086e-09
hist(data.positioning.matched$match_accuracy_result, breaks = 100,
main = "Histogram (Zoomed)", xlab = "Accuracy",
col = "lightblue", xlim = c(0, 0.06))plot(density(data.positioning.matched$match_accuracy_result),
main = "Density Plot (Zoomed)", xlab = "Accuracy",
col = "blue", lwd = 2, xlim = c(0, 0.6))Although the w value is close to 1, the p value is below 0.05 so we reject null hypothesis that the data is normally distributed So, we cannot use one way ANOVA, instead, we use Wilcoxon signed-rank test
data.positioning.matched.accuracy_avg.long <- data.positioning.matched %>%
group_by(UserID, positionCondition, distance, direction) %>%
summarise(mean_accuracy = mean(match_accuracy_result, na.rm = TRUE), .groups = 'drop')
data.positioning.matched.art <- art(mean_accuracy ~ positionCondition * distance * direction + (1|UserID), data = data.positioning.matched.accuracy_avg.long)
anova(data.positioning.matched.art)## Analysis of Variance of Aligned Rank Transformed Data
##
## Table Type: Analysis of Deviance Table (Type III Wald F tests with Kenward-Roger df)
## Model: Mixed Effects (lmer)
## Response: art(mean_accuracy)
##
## F Df Df.res Pr(>F)
## 1 positionCondition 0.88851 1 99.000 0.3481756
## 2 distance 8.58240 1 99.545 0.0042088 **
## 3 direction 3.51399 2 99.527 0.0335248 *
## 4 positionCondition:distance 0.88433 1 99.000 0.3493079
## 5 positionCondition:direction 0.21943 2 99.000 0.8033694
## 6 distance:direction 0.25114 2 99.512 0.7784052
## 7 positionCondition:distance:direction 1.62425 2 99.000 0.2022653
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# • The main effect of positionCondition was not statistically significant: F(1, 99) = 0.89, p = 0.348.
# • The main effect of distance was statistically significant: F(1, 99.55) = 8.58, p = 0.0042 (**).
# • The main effect of direction was also statistically significant: F(2, 99.53) = 3.51, p = 0.0335 (*).
# • The interaction between positionCondition and distance was not significant: F(1, 99) = 0.88, p = 0.349.
# • The interaction between positionCondition and direction was not significant: F(2, 99) = 0.22, p = 0.803.
# • The interaction between distance and direction was not significant: F(2, 99.51) = 0.25, p = 0.778.
# • The three-way interaction (positionCondition × distance × direction) was not significant: F(2, 99) = 1.62, p = 0.202.
# There were significant main effects of both distance and direction on mean accuracy, indicating that participants’ accuracy was influenced independently by how far the task was and from which direction it came. However, positionCondition had no significant effect, and no interaction terms reached statistical significance. This suggests that the combined effects of position, distance, and direction do not differentially impact accuracy beyond the main effects of distance and direction alone.
ggplot(data.positioning.matched.accuracy_avg.long, aes(x = positionCondition, y = mean_accuracy, group = UserID)) +
geom_line(aes(color = as.factor(UserID)), linewidth = 1, alpha = 0.6) +
geom_point(size = 3) +
facet_wrap(~ distance + direction) +
labs(title = "Paired Accuracy by Globe Behaviour, Globes' Distance and Direction",
x = "Behaviour",
y = "Match Accuracy") +
theme_minimal() +
theme(strip.text = element_text(size = 12, face = "bold"))ggplot(data.positioning.matched.accuracy_avg.long, aes(x = positionCondition, y = mean_accuracy)) +
geom_boxplot(outlier.shape = NA, fill = "lightblue") +
geom_jitter(width = 0.1, size = 2, alpha = 0.7) +
facet_wrap(~ distance + direction) +
labs(title = "Boxplots of Accuracy by Globe Behaviour, Globes' Distance and Direction",
x = "Behaviour",
y = "Match Accuracy") +
theme_minimal()data.positioning.taskCompletion_avg <- data.positioning %>%
group_by(UserID, TaskID) %>%
summarise(
completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
.groups = "drop"
)
shapiro.test(data.positioning.taskCompletion_avg$completion_time)##
## Shapiro-Wilk normality test
##
## data: data.positioning.taskCompletion_avg$completion_time
## W = 0.59479, p-value < 2.2e-16
data.positioning.taskCompletion_avg.long <- data.positioning %>%
group_by(UserID, positionCondition, TaskID, distance, direction) %>%
summarise(
completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
.groups = "drop"
) %>%
group_by(UserID, positionCondition, distance, direction) %>%
summarise(
avg_completion_time = mean(completion_time),
.groups = "drop"
)
data.positioning.taskCompletion_avg.art <- art(avg_completion_time ~ positionCondition * distance * direction + (1|UserID), data = data.positioning.taskCompletion_avg.long)
anova(data.positioning.taskCompletion_avg.art)## Analysis of Variance of Aligned Rank Transformed Data
##
## Table Type: Analysis of Deviance Table (Type III Wald F tests with Kenward-Roger df)
## Model: Mixed Effects (lmer)
## Response: art(avg_completion_time)
##
## F Df Df.res Pr(>F)
## 1 positionCondition 0.61363 1 99.002 0.4352944
## 2 distance 1.75116 1 100.619 0.1887297
## 3 direction 6.38814 2 100.863 0.0024424 **
## 4 positionCondition:distance 0.24125 1 99.002 0.6243926
## 5 positionCondition:direction 0.50478 2 99.002 0.6051833
## 6 distance:direction 0.57384 2 100.707 0.5651868
## 7 positionCondition:distance:direction 0.43726 2 99.002 0.6470467
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# • positionCondition had no significant effect on average task completion time:F(1, 99.00) = 0.61, p = 0.435
# • distance also showed no significant effect:F(1, 100.62) = 1.75, p = 0.189
# • direction showed a statistically significant main effect:F(2, 100.86) = 6.39, p = 0.0024 (**), suggesting direction influences how long tasks take
# • The interaction between positionCondition and distance was not significant:F(1, 99.00) = 0.24, p = 0.624
# • The interaction between positionCondition and direction was not significant:F(2, 99.00) = 0.50, p = 0.605
# • The interaction between distance and direction was not significant:F(2, 100.71) = 0.57, p = 0.565
# • The three-way interaction (positionCondition × distance × direction) was also not significant:F(2, 99.00) = 0.44, p = 0.647
#
# Only direction had a statistically significant effect on average task completion time, indicating that the direction from which the task was approached meaningfully influenced how long participants took to complete it. Other factors—positionCondition, distance, and all interaction terms—did not have significant effects. This suggests that regardless of position or distance, direction alone may account for differences in task completion time in this context.
ggplot(data.positioning.taskCompletion_avg.long, aes(x = positionCondition, y = avg_completion_time, group = UserID)) +
geom_line(aes(color = as.factor(UserID)), linewidth = 1, alpha = 0.6) +
geom_point(size = 3) +
facet_wrap(~ distance + direction) +
labs(
title = "Paired Task Completion Time by Behaviour, Distance, and Direction",
x = "Behaviour",
y = "Completion Time (minutes)",
color = "UserID"
) +
theme_minimal()ggplot(data.positioning.taskCompletion_avg.long, aes(x = positionCondition, y = avg_completion_time)) +
geom_boxplot(outlier.shape = NA, fill = "lightblue") +
geom_jitter(width = 0.1, size = 2, alpha = 0.7) +
facet_wrap(~ distance + direction) +
labs(title = "Boxplots of Task Completion Time by Behaviour, Distance, and Directio",
x = "Behaviour",
y = "Task Completion Time") +
theme_minimal()data.positioning.long <- data.positioning %>%
select(UserID, positionCondition, PAAS_RG, BORG_RG, PAAS_NRG, BORG_NRG) %>%
distinct() %>%
pivot_longer(
cols = c(PAAS_RG, BORG_RG, PAAS_NRG, BORG_NRG),
names_to = "Measure",
values_to = "Score"
) %>%
filter(!is.na(Score)) %>%
mutate(
ExertionType = case_when(
str_detect(Measure, "PAAS") ~ "Cognitive load",
str_detect(Measure, "BORG") ~ "Physical exertion",
TRUE ~ "Unknown"
),
UserID = as.factor(UserID),
ExertionType = as.factor(ExertionType)
) %>%
group_by(UserID, positionCondition, Measure)
data.positioning.long.art_anova <- art(Score ~ positionCondition * ExertionType + (1|UserID), data = data.positioning.long)
anova(data.positioning.long.art_anova)## Analysis of Variance of Aligned Rank Transformed Data
##
## Table Type: Analysis of Deviance Table (Type III Wald F tests with Kenward-Roger df)
## Model: Mixed Effects (lmer)
## Response: art(Score)
##
## F Df Df.res Pr(>F)
## 1 positionCondition 12.54732 1 33 0.0012077 **
## 2 ExertionType 12.80843 1 33 0.0010921 **
## 3 positionCondition:ExertionType 0.17619 1 33 0.6773922
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# • Main effect of positionCondition: F(1, 33) = 12.55, p = 0.0012 This is statistically significant, indicating that task positioning condition (e.g., rotating vs non-rotating globe) has a significant effect on the overall exertion score (which includes cognitive load and physical exertion).
# • Main effect of ExertionType: F(1, 33) = 12.81, p = 0.0011 Also statistically significant, meaning that there is a clear difference between cognitive load (PAAS) and physical exertion (BORG) scores regardless of the positioning condition.
# • Interaction effect (positionCondition × ExertionType): F(1, 33) = 0.18, p = 0.6774, suggesting that the effect of positioning condition does not depend on the type of exertion, and vice versa. The differences in PAAS and BORG scores are consistent across both positioning conditions.
# Both the positioning condition and type of exertion significantly influence the exertion score independently, but there is no interaction effect. This means that while both factors matter, their effects do not amplify or diminish each other. For example, rotating vs non-rotating globes affects scores, and cognitive vs physical exertion shows distinct levels, but the difference between PAAS and BORG scores remains similar across positioning conditions.
ggplot(data.positioning.long, aes(x = positionCondition, y = Score, fill = ExertionType)) +
geom_boxplot(width = 0.1, position = position_dodge(0.8)) +
labs(
x = "Position Condition",
y = "Score",
title = "Distribution of Scores by Behaviour and Exertion Type"
) +
theme_minimal()data.positioning %>%
select(UserID, behaviour_preference) %>%
distinct() %>%
count(behaviour_preference) %>%
mutate(
percent = n / sum(n),
ncount = paste0(n, "\n", percent_format()(percent))
) %>%
ggplot(aes(x = "", y = n, fill = behaviour_preference)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = ncount), position = position_stack(vjust = 0.5), size = 4) +
labs(
title = "Distribution of Positioning Behaviour Preferences",
fill = "Preference"
) +
theme_void()data.rotating <- data %>%
mutate(rotationCondition = if_else(oneHandedRotationGesture, "oneHanded", "twoHanded")) %>%
select(-oneHandedRotationGesture) %>%
inner_join(demographic, by = "UserID") %>%
inner_join(rotation_OH, by = "UserID") %>%
rename(
PAAS_OH = Mentally_demanding,
BORG_OH = Physically_demanding
) %>%
mutate(
PAAS_OH = as.numeric(str_extract(PAAS_OH, "\\d+(\\.\\d+)?")),
BORG_OH = as.numeric(str_extract(BORG_OH, "\\d+(\\.\\d+)?"))
) %>%
mutate(
PAAS_OH = if_else(rotationCondition == "oneHanded", PAAS_OH, NA_real_),
BORG_OH = if_else(rotationCondition == "oneHanded", BORG_OH, NA_real_)
) %>%
inner_join(rotation_TH, by = "UserID") %>%
rename(
PAAS_TH = Mentally_demanding,
BORG_TH = Physically_demanding
) %>%
mutate(
PAAS_TH = as.numeric(str_extract(PAAS_TH, "\\d+(\\.\\d+)?")),
BORG_TH = as.numeric(str_extract(BORG_TH, "\\d+(\\.\\d+)?"))
) %>%
mutate(
PAAS_TH = if_else(rotationCondition == "twoHanded", PAAS_TH, NA_real_),
BORG_TH = if_else(rotationCondition == "twoHanded", BORG_TH, NA_real_)
) %>%
inner_join(rotation_preference, by = "UserID") %>%
rename(
behaviour_preference = Rotation_preference,
behaviour_feedback = Rotation_feedback
) %>%
mutate(
behaviour_preference = case_when(
str_detect(behaviour_preference, "One-handed") ~ "oneHandedPreference",
str_detect(behaviour_preference, "Two-handed") ~ "twoHandedPreference",
str_detect(behaviour_preference, "no preference") ~ "noPreference",
TRUE ~ "unknown"
)) %>%
filter(Type == "rotationTask") %>%
select(UserID, TaskID, ActionID, rotationCondition, complexity, Date, ActionStatus, main_rotation_x,
main_rotation_y, main_rotation_z, main_rotation_w, target_rotation_x, target_rotation_y, target_rotation_z,
target_rotation_w,match_accuracy_result, status, PAAS_OH, BORG_OH, PAAS_TH, BORG_TH, behaviour_preference, behaviour_feedback) %>%
mutate(complexity = as.factor(complexity),
rotationCondition = as.factor(rotationCondition),
status = as.factor(status),
behaviour_preference = as.factor(behaviour_preference))data.rotating.matched <- data.rotating %>%
filter(status == "Matched")
shapiro.test(data.rotating.matched$match_accuracy_result)##
## Shapiro-Wilk normality test
##
## data: data.rotating.matched$match_accuracy_result
## W = 0.94156, p-value = 5.023e-07
hist(data.rotating.matched$match_accuracy_result, breaks = 100,
main = "Histogram (Zoomed)", xlab = "Accuracy",
col = "lightblue", xlim = c(0, 0.5))plot(density(data.rotating.matched$match_accuracy_result),
main = "Density Plot (Zoomed)", xlab = "Accuracy",
col = "blue", lwd = 2, xlim = c(0, 0.5))Although the w value is close to 1, the p value is below 0.05 so we reject null hypothesis that the data is normally distributed So, we cannot use one way ANOVA, instead, we use Wilcoxon signed-rank test
data.rotating.matched.accuracy_avg.long <- data.rotating.matched %>%
group_by(UserID, rotationCondition, complexity) %>%
summarise(mean_accuracy = mean(match_accuracy_result, na.rm = TRUE), .groups = 'drop')
data.rotating.matched.art <- art(mean_accuracy ~ rotationCondition * complexity + (1|UserID), data = data.rotating.matched.accuracy_avg.long)
anova(data.rotating.matched.art)## boundary (singular) fit: see help('isSingular')
## boundary (singular) fit: see help('isSingular')
## boundary (singular) fit: see help('isSingular')
## Analysis of Variance of Aligned Rank Transformed Data
##
## Table Type: Analysis of Deviance Table (Type III Wald F tests with Kenward-Roger df)
## Model: Mixed Effects (lmer)
## Response: art(mean_accuracy)
##
## F Df Df.res Pr(>F)
## 1 rotationCondition 4.67385 1 33 0.037975 *
## 2 complexity 20.74234 1 33 6.8141e-05 ***
## 3 rotationCondition:complexity 0.25022 1 33 0.620238
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# • rotationCondition: F(1, 33) = 4.67, p = 0.038 Significant: rotation affects accuracy
# • complexity: F(1, 33) = 20.74, p < 0.001 Highly significant: complexity affects accuracy
# • rotationCondition × complexity: F(1, 33) = 0.25, p = 0.62 Not significant: no interaction effect
# The ART ANOVA revealed significant main effects of both rotation condition and complexity on accuracy. Rotation condition had a modest but significant effect (p = 0.038), while complexity showed a strong influence (p < 0.001). However, there was no significant interaction between rotation and complexity (p = 0.62), indicating that the effects of each factor on accuracy are independent and do not influence each other.
ggplot(data.rotating.matched.accuracy_avg.long, aes(x = rotationCondition, y = mean_accuracy, group = UserID)) +
geom_line(aes(color = as.factor(UserID)), linewidth = 1, alpha = 0.6) +
geom_point(size = 3) +
facet_wrap(~ complexity) +
labs(
title = "Paired Task Match Accuracy by Technique and Complexity",
x = "Technique",
y = "Accuracy",
color = "UserID"
) +
theme_minimal() +
theme(strip.text = element_text(size = 12, face = "bold"))ggplot(data.rotating.matched.accuracy_avg.long, aes(x = rotationCondition, y = mean_accuracy)) +
geom_boxplot(outlier.shape = NA, fill = "lightblue") +
geom_jitter(width = 0.1, size = 2, alpha = 0.7) +
facet_wrap(~ complexity) +
labs(title = "Boxplots of Task Accuracy by Technique and Complexity",
x = "Technique",
y = "Match Accuracy") +
theme_minimal()data.rotating.taskCompletion_avg <- data.rotating %>%
group_by(UserID, TaskID) %>%
summarise(
completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
.groups = "drop"
)
shapiro.test(data.rotating.taskCompletion_avg$completion_time)##
## Shapiro-Wilk normality test
##
## data: data.rotating.taskCompletion_avg$completion_time
## W = 0.49195, p-value < 2.2e-16
data.rotating.taskCompletion_avg.long <- data.rotating %>%
group_by(UserID, rotationCondition, complexity, TaskID) %>%
summarise(
completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
.groups = "drop"
) %>%
group_by(UserID, rotationCondition, complexity) %>%
summarise(
avg_completion_time = mean(completion_time),
.groups = "drop"
)
data.rotating.taskCompletion_avg.art <- art(avg_completion_time ~ rotationCondition * complexity + (1|UserID), data = data.rotating.taskCompletion_avg.long)
anova(data.rotating.taskCompletion_avg.art)## boundary (singular) fit: see help('isSingular')
## boundary (singular) fit: see help('isSingular')
## boundary (singular) fit: see help('isSingular')
## Analysis of Variance of Aligned Rank Transformed Data
##
## Table Type: Analysis of Deviance Table (Type III Wald F tests with Kenward-Roger df)
## Model: Mixed Effects (lmer)
## Response: art(avg_completion_time)
##
## F Df Df.res Pr(>F)
## 1 rotationCondition 8.4424 1 33 0.006499 **
## 2 complexity 37.7528 1 33 6.3129e-07 ***
## 3 rotationCondition:complexity 2.3552 1 33 0.134399
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# • rotationCondition: F(1, 33) = 8.44, p = 0.0065 (**) Statistically significant — rotation has a clear effect on completion time
# • complexity: F(1, 33) = 37.75, p < 0.0001 (***) Statistically significant — complexity strongly affects completion time
# • rotationCondition × complexity: F(1, 33) = 2.36, p = 0.1344 Not significant — no evidence of interaction between rotation and complexity
ggplot(data.rotating.taskCompletion_avg.long,
aes(x = rotationCondition, y = avg_completion_time, group = UserID)) +
geom_line(aes(color = as.factor(UserID)), linewidth = 1, alpha = 0.6) +
geom_point(size = 3) +
facet_wrap(~ complexity) +
labs(
title = "Paired Task Completion Time by Technique and Complexity",
x = "Technique",
y = "Average Completion Time (minutes)",
color = "UserID"
) +
theme_minimal() +
theme(strip.text = element_text(size = 12, face = "bold"))ggplot(data.rotating.taskCompletion_avg.long, aes(x = rotationCondition, y = avg_completion_time)) +
geom_boxplot(outlier.shape = NA, fill = "lightblue") +
geom_jitter(width = 0.1, size = 2, alpha = 0.7) +
facet_wrap(~ complexity) +
labs(title = "Boxplots of Task Completion Time by Technique and Complexity",
x = "Technique",
y = "Task Completion Time") +
theme_minimal()data.rotating.long <- data.rotating %>%
select(UserID, rotationCondition, PAAS_OH, BORG_OH, PAAS_TH, BORG_TH) %>%
distinct() %>%
pivot_longer(
cols = c(PAAS_OH, BORG_OH, PAAS_TH, BORG_TH),
names_to = "Measure",
values_to = "Score"
) %>%
filter(!is.na(Score)) %>%
mutate(
ExertionType = case_when(
str_detect(Measure, "PAAS") ~ "Cognitive load",
str_detect(Measure, "BORG") ~ "Physical exertion",
TRUE ~ "Unknown"
),
UserID = as.factor(UserID),
ExertionType = as.factor(ExertionType)
) %>%
group_by(UserID, rotationCondition, Measure)
data.rotating.long.art_anova <- art(Score ~ rotationCondition * ExertionType + (1|UserID), data = data.rotating.long)
anova(data.rotating.long.art_anova)## Analysis of Variance of Aligned Rank Transformed Data
##
## Table Type: Analysis of Deviance Table (Type III Wald F tests with Kenward-Roger df)
## Model: Mixed Effects (lmer)
## Response: art(Score)
##
## F Df Df.res Pr(>F)
## 1 rotationCondition 4.276241 1 33 0.046561 *
## 2 ExertionType 20.747724 1 33 6.8023e-05 ***
## 3 rotationCondition:ExertionType 0.016351 1 33 0.899028
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# • Main effect of rotationCondition: F(1, 33) = 4.28, p = 0.0466 This is statistically significant at the 0.05 level, indicating that rotating vs non-rotating globe conditions have a meaningful impact on participants’ perceived exertion scores (cognitive or physical).
# • Main effect of ExertionType: F(1, 33) = 20.75, p < 0.001 This is highly significant, showing a strong difference between cognitive load (PAAS) and physical exertion (BORG) scores, regardless of the globe rotation condition.
# • Interaction effect (rotationCondition × ExertionType): F(1, 33) = 0.016, p = 0.8990 Not significant. Suggesting that the difference between cognitive and physical exertion is consistent across both rotation conditions. The type of exertion does not change depending on whether the globe was rotating or not.
# Both rotation condition and exertion type have independent effects on perceived exertion. Participants reported different exertion levels between rotating and non-rotating globes, and also between cognitive and physical demands. However, there is no interaction, meaning the relative difference between PAAS and BORG remains stable across rotation types.
ggplot(data.rotating.long, aes(x = rotationCondition, y = Score, fill = ExertionType)) +
geom_boxplot(width = 0.1, position = position_dodge(0.8)) +
labs(
x = "Rotation Condition",
y = "Score",
title = "Distribution of Scores by Technique and Exertion Type"
) +
theme_minimal()data.rotating %>%
select(UserID, behaviour_preference) %>%
distinct() %>%
count(behaviour_preference) %>%
mutate(
percent = n / sum(n),
ncount = paste0(n, "\n", percent_format()(percent))
) %>%
ggplot(aes(x = "", y = n, fill = behaviour_preference)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = ncount), position = position_stack(vjust = 0.5), size = 4) +
labs(
title = "Distribution of Rotation Behaviour Preferences",
fill = "Preference"
) +
theme_void()data.rotating.preference.summary <- data.rotating %>%
mutate(
behaviour_preference = case_when(
behaviour_preference == "oneHandedPreference" ~ "One Handed Gesture",
behaviour_preference == "twoHandedPreference" ~ "Two Handed Gesture",
behaviour_preference == "noPreference" ~ "No Preference",
TRUE ~ behaviour_preference
)
) %>%
group_by(UserID) %>%
summarise(
behaviour_preference = first(behaviour_preference),
behaviour_feedback = first(behaviour_feedback),
.groups = "drop"
)
kable(data.rotating.preference.summary, caption = "User Feedback Summary - Rotating")| UserID | behaviour_preference | behaviour_feedback |
|---|---|---|
| 1 | One Handed Gesture | I feel more convenient to use one-handed rotation gesture because it is less confusing compared to two-handed rotation gesture, where I had a bit more difficulties in balancing my hands. |
| 2 | Two Handed Gesture | I have more control with the two-handed rotation gesture, it feels more natural. But still feel limited In terms of flexibility upon rotation. I think more gesture such as moving the globe position when both hands are moving simultaneously following the centre of the hands. |
| 3 | One Handed Gesture | More fingers means more calorie burns. But it has limitation with the control, not sure how to solve or give gesture recommendation. |
| 4 | One Handed Gesture | I like one handed better because I have more control to rotate the orientations as I like, as for the gestures I ’ll suggest maybe we can use hands like waving gestures to rotate the globe |
| 5 | No Preference | Both options have their advantages. One hand is simple but little bit harder for complex task like rotation. I think it would be better if I can rotate our pump like rotating door knob |
| 6 | One Handed Gesture | I prefer one-handed gesture one because its easier to imagine the direction. However, the two-handed gesture will be useful in medical field. Especially in surgery. |
| 7 | One Handed Gesture | I prefer one-handed gesture because it is handy and more flexible. |
| 8 | Two Handed Gesture | I prefer two handed because it gives more flexibility. However I feel that two handed takes time to adapt. I think it would be better if we can touch and manipulate directly like aphysical globe. If the globe is far, we can use gaze and pinch to make it nearer, then we can use direct gesture manipulation. |
| 9 | One Handed Gesture | I prefer one handed because it is simpler. |
| 10 | One Handed Gesture | I prefer one handed because it is easier to move the globe from any directions while the two handed it is more difficult because it takes two-hands coordination. Gaze and pinch is convinient enough. |
| 11 | One Handed Gesture | I prefer one handed because thats how I usually operate globe in real life. Unless the two handed uses palms like holding real globes, I’d prefer it. |
| 12 | No Preference | 1. It would be better if we have the option of using two hands, instead of directly using 2 hands. It gives the option of z axis adjustment in the middle of x,y axis rotation. |
data.scale <- data %>%
mutate(scaleCondition = if_else(moveGlobeWhileScaling, "movingGlobe", "nonMovingGlobe")) %>%
select(-moveGlobeWhileScaling) %>%
inner_join(demographic, by = "UserID") %>%
inner_join(scale_MG, by = "UserID") %>%
rename(
PAAS_MG = Mentally_demanding,
BORG_MG = Physically_demanding
) %>%
mutate(
PAAS_MG = as.numeric(str_extract(PAAS_MG, "\\d+(\\.\\d+)?")),
BORG_MG = as.numeric(str_extract(BORG_MG, "\\d+(\\.\\d+)?"))
) %>%
mutate(
PAAS_MG = if_else(scaleCondition == "movingGlobe", PAAS_MG, NA_real_),
BORG_MG = if_else(scaleCondition == "movingGlobe", BORG_MG, NA_real_)
) %>%
inner_join(scale_NMG, by = "UserID") %>%
rename(
PAAS_NMG = Mentally_demanding,
BORG_NMG = Physically_demanding
) %>%
mutate(
PAAS_NMG = as.numeric(str_extract(PAAS_NMG, "\\d+(\\.\\d+)?")),
BORG_NMG = as.numeric(str_extract(BORG_NMG, "\\d+(\\.\\d+)?"))
) %>%
mutate(
PAAS_NMG = if_else(scaleCondition == "nonMovingGlobe", PAAS_NMG, NA_real_),
BORG_NMG = if_else(scaleCondition == "nonMovingGlobe", BORG_NMG, NA_real_)
) %>%
inner_join(scale_preference, by = "UserID") %>%
rename(
behaviour_preference = Scale_preference,
behaviour_feedback = Scale_feedback
) %>%
mutate(
behaviour_preference = case_when(
str_detect(behaviour_preference, "Maintain distance") ~ "maintainDistance",
str_detect(behaviour_preference, "Maintain globe") ~ "maintainGlobe",
str_detect(behaviour_preference, "no preference") ~ "noPreference",
TRUE ~ "unknown"
)) %>%
filter(Type == "scaleTask") %>%
select(UserID, TaskID, ActionID, scaleCondition, zoomDirection, Date, ActionStatus, main_scale_x,
main_scale_y, main_scale_z, target_scale_x, target_scale_y, target_scale_z, match_accuracy_result, status,
PAAS_MG, BORG_MG, PAAS_NMG, BORG_NMG, behaviour_preference, behaviour_feedback) %>%
mutate(zoomDirection = as.factor(zoomDirection),
scaleCondition = as.factor(scaleCondition),
status = as.factor(status),
behaviour_preference = as.factor(behaviour_preference))data.scale.matched <- data.scale %>%
filter(status == "Matched")
shapiro.test(data.scale.matched$match_accuracy_result)##
## Shapiro-Wilk normality test
##
## data: data.scale.matched$match_accuracy_result
## W = 0.94732, p-value = 1.64e-06
hist(data.scale.matched$match_accuracy_result, breaks = 100,
main = "Histogram (Zoomed)", xlab = "Accuracy",
col = "lightblue", xlim = c(0, 0.5))plot(density(data.scale.matched$match_accuracy_result),
main = "Density Plot (Zoomed)", xlab = "Accuracy",
col = "blue", lwd = 2, xlim = c(0, 0.5))data.scale.matched.accuracy_avg.long <- data.scale.matched %>%
group_by(UserID, scaleCondition, zoomDirection) %>%
summarise(mean_accuracy = mean(match_accuracy_result, na.rm = TRUE), .groups = 'drop')
data.scale.matched.art <- art(mean_accuracy ~ scaleCondition * zoomDirection + (1|UserID), data = data.scale.matched.accuracy_avg.long)
anova(data.scale.matched.art)## Analysis of Variance of Aligned Rank Transformed Data
##
## Table Type: Analysis of Deviance Table (Type III Wald F tests with Kenward-Roger df)
## Model: Mixed Effects (lmer)
## Response: art(mean_accuracy)
##
## F Df Df.res Pr(>F)
## 1 scaleCondition 0.54873 1 33 0.46407
## 2 zoomDirection 0.51402 1 33 0.47845
## 3 scaleCondition:zoomDirection 0.95304 1 33 0.33605
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# • The main effect of scaleCondition on mean accuracy was not significant, F(1, 33) = 0.55, p = 0.464.
# • The main effect of zoomDirection was also not significant, F(1, 33) = 0.51, p = 0.478.
# • The interaction between scaleCondition and zoomDirection was not significant either, F(1, 33) = 0.95, p = 0.336.
# There were no statistically significant effects of either scale condition, zoom direction, or their interaction on mean accuracy. This suggests that participants’ accuracy was not influenced by how the globe was scaled or zoomed, nor by the combination of these two factors.
ggplot(data.scale.matched.accuracy_avg.long, aes(x = scaleCondition, y = mean_accuracy, group = UserID)) +
geom_line(aes(color = as.factor(UserID)), linewidth = 1, alpha = 0.6) +
geom_point(size = 3) +
facet_wrap(~ zoomDirection) +
labs(title = "Paired Accuracy by Globe Behaviour and Zoom Direction",
x = "Behaviour",
y = "Match Accuracy",
color = "UserID") +
theme_minimal() +
theme(strip.text = element_text(size = 12, face = "bold"))ggplot(data.scale.matched.accuracy_avg.long, aes(x = scaleCondition, y = mean_accuracy)) +
geom_boxplot(outlier.shape = NA, fill = "lightblue") +
geom_jitter(width = 0.1, size = 2, alpha = 0.7) +
facet_wrap(~ zoomDirection) +
labs(title = "Boxplots of Accuracy by Globe Movement Behaviour",
x = "Behaviour",
y = "Match Accuracy") +
theme_minimal()data.scale.taskCompletion_avg <- data.scale %>%
group_by(UserID, TaskID) %>%
summarise(
completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
.groups = "drop"
)
shapiro.test(data.scale.taskCompletion_avg$completion_time)##
## Shapiro-Wilk normality test
##
## data: data.scale.taskCompletion_avg$completion_time
## W = 0.69808, p-value < 2.2e-16
data.scale.taskCompletion_avg.long <- data.scale %>%
group_by(UserID, scaleCondition, TaskID, zoomDirection) %>%
summarise(
completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
.groups = "drop"
) %>%
group_by(UserID, scaleCondition, zoomDirection) %>%
summarise(
avg_completion_time = mean(completion_time),
.groups = "drop"
)
data.scale.taskCompletion.art <- art(avg_completion_time ~ scaleCondition * zoomDirection + (1|UserID), data = data.scale.taskCompletion_avg.long)
anova(data.scale.matched.art)## Analysis of Variance of Aligned Rank Transformed Data
##
## Table Type: Analysis of Deviance Table (Type III Wald F tests with Kenward-Roger df)
## Model: Mixed Effects (lmer)
## Response: art(mean_accuracy)
##
## F Df Df.res Pr(>F)
## 1 scaleCondition 0.54873 1 33 0.46407
## 2 zoomDirection 0.51402 1 33 0.47845
## 3 scaleCondition:zoomDirection 0.95304 1 33 0.33605
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# scaleCondition F-value 0.549 P-value 0.464 means not significant
# zoomDirection F-value 0.514 P-value 0.478 means Not significant
# scaleCondition:zoomDirection (Interaction) F-value 0.953 Not significant
# The analysis revealed no statistically significant effects of scaleCondition, zoomDirection, or their interaction on mean accuracy. All p-values are well above the common alpha level of 0.05, indicating that neither the type of scaling nor the zoom direction had a meaningful impact on participants’ accuracy. Additionally, the lack of a significant interaction suggests that the combined effect of scaling and zoom direction does not influence accuracy either.
ggplot(data.scale.taskCompletion_avg.long, aes(x = scaleCondition, y = avg_completion_time, group = UserID)) +
geom_line(aes(color = as.factor(UserID)), linewidth = 1, alpha = 0.6) +
geom_point(size = 3) +
facet_wrap(~ zoomDirection) +
labs(
title = "Paired Task Completion Time by Behaviour and Zoom Direction",
x = "Behaviour",
y = "Completion Time (minutes)",
color = "UserID"
) +
theme_minimal()ggplot(data.scale.taskCompletion_avg.long, aes(x = scaleCondition, y = avg_completion_time)) +
geom_boxplot(outlier.shape = NA, fill = "lightblue") +
geom_jitter(width = 0.1, size = 2, alpha = 0.7) +
facet_wrap(~ zoomDirection) +
labs(title = "Boxplots of Task Completion Time by Globe's Behaviour and Zoom Direction",
x = "Behaviour",
y = "Task Completion Time") +
theme_minimal()data.scale.long <- data.scale %>%
select(UserID, scaleCondition, PAAS_MG, BORG_MG, PAAS_NMG, BORG_NMG) %>%
distinct() %>%
pivot_longer(
cols = c(PAAS_MG, BORG_MG, PAAS_NMG, BORG_NMG),
names_to = "Measure",
values_to = "Score"
) %>%
filter(!is.na(Score)) %>%
mutate(
ExertionType = case_when(
str_detect(Measure, "PAAS") ~ "Cognitive load",
str_detect(Measure, "BORG") ~ "Physical exertion",
TRUE ~ "Unknown"
),
UserID = as.factor(UserID),
ExertionType = as.factor(ExertionType)
) %>%
group_by(UserID, scaleCondition, Measure)
data.scale.long.art_anova <- art(Score ~ scaleCondition * ExertionType + (1|UserID), data = data.scale.long)
anova(data.scale.long.art_anova)## Analysis of Variance of Aligned Rank Transformed Data
##
## Table Type: Analysis of Deviance Table (Type III Wald F tests with Kenward-Roger df)
## Model: Mixed Effects (lmer)
## Response: art(Score)
##
## F Df Df.res Pr(>F)
## 1 scaleCondition 1.358691 1 33 0.25212
## 2 ExertionType 55.274139 1 33 1.5246e-08 ***
## 3 scaleCondition:ExertionType 0.084177 1 33 0.77353
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# • Main effect of scaleCondition: F(1, 33) = 1.36, p = 0.2521 Not statistically significant. This suggests that the scaling condition (e.g. scaled vs not scaled globe) did not have a meaningful effect on exertion scores overall.
# • Main effect of ExertionType: F(1, 33) = 55.27, p < 0.001 → Highly significant. This shows a strong difference between cognitive load (PAAS) and physical exertion (BORG) scores, regardless of the scale condition.
# • Interaction (scaleCondition × ExertionType): F(1, 33) = 0.084, p = 0.7735. This means the difference between PAAS and BORG scores does not vary depending on whether scaling was involved.
# Only the type of exertion (cognitive vs physical) had a significant impact on participant scores. The scale condition had no effect, nor did it change the relationship between exertion types. Participants consistently rated cognitive and physical exertion differently, but this pattern was stable across both scaled and non-scaled conditions.
ggplot(data.scale.long, aes(x = scaleCondition, y = Score, fill = ExertionType)) +
geom_boxplot(width = 0.1, position = position_dodge(0.8)) +
labs(
x = "Scale Condition",
y = "Score",
title = "Distribution of Scores by Behaviour and Exertion Type"
) +
theme_minimal()data.scale %>%
select(UserID, behaviour_preference) %>%
distinct() %>%
count(behaviour_preference) %>%
mutate(
percent = n / sum(n),
ncount = paste0(n, "\n", percent_format()(percent))
) %>%
ggplot(aes(x = "", y = n, fill = behaviour_preference)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = ncount), position = position_stack(vjust = 0.5), size = 4) +
labs(
title = "Distribution of Scale Behaviour Preferences",
fill = "Preference"
) +
theme_void()data.scale.preference.summary <- data.scale %>%
mutate(
behaviour_preference = case_when(
behaviour_preference == "maintainDistance" ~ "Maintain Globe's Distance",
behaviour_preference == "maintainGlobe" ~ "Maintain Globe's Position",
behaviour_preference == "noPreference" ~ "No Preference",
TRUE ~ behaviour_preference
)
) %>%
group_by(UserID) %>%
summarise(
behaviour_preference = first(behaviour_preference),
behaviour_feedback = first(behaviour_feedback),
.groups = "drop"
)
kable(data.scale.preference.summary, caption = "User Feedback Summary - Scale")| UserID | behaviour_preference | behaviour_feedback |
|---|---|---|
| 1 | Maintain Globe’s Distance | I prefer maintain globe position since it makes me easy to observe the globe closely and clearly, because I think the maintain distance one is not close enough and a bit blurry. |
| 2 | Maintain Globe’s Distance | For me personally I like to use the maintain distance to globe behaviour because its easier to see when observing the surface. But depends on the situation, if we are in a bigger room such as auditorium it will be more managable. But since in this I’m doing it in a small room, its easier to use the maintain distance to globe. |
| 3 | Maintain Globe’s Distance | For the scope of this globe experiment, I prefer “maintain distance…”, because I do not think it is necessary to go inside the globe which is empty. However, the zoom level for “maintain distance…” behaviour needs to be closer or have zoom level control, I.e, “Observing a very small island in the globe, like Bermuda island” |
| 4 | Maintain Globe’s Distance | I like the 2nd options better so we can observe the globe more detail, without being worry about the globe disappear in front of us. |
| 5 | Maintain Globe’s Position | I prefer maintain globe position because the zoom level is larger, so I can easily observe the object |
| 6 | Maintain Globe’s Distance | For specifically observing maps/globes, I prefer the maiaintain distance to globes one because, ithe maximum zoom level is enough for me to observe the surface of the globe. |
| 7 | Maintain Globe’s Distance | I prefer the maintain distance to globe because it is easier to observe t, the zoom level of maintain distance to globe is good. |
| 8 | No Preference | Depends on the situation. I have no preference. Maintain distance is confusing whether the gesture is broken or not at the maximum zoom point. But if the purpose is for observing the globe the maintain distance is better. |
| 9 | Maintain Globe’s Distance | I prefer maintain distance |
| 10 | Maintain Globe’s Distance | I prefer the maintain distance because it is easier to observe the surface in a proper distance |
| 11 | No Preference | It depends, for professionals like maybe government, if they want to observe details, it would better use maintain globe. But for casual users, they would not like the globe zoomed through their heads, they would like maintain distance better. So, I have no preference. |
| 12 | Maintain Globe’s Distance | 1. It would be better if the limit of the zoom is very close (increase the limit) to our face or at least give the option to. |
Comments